package opencvtest.chapter04;

import static com.googlecode.javacv.cpp.opencv_core.cvGetSize;
import static com.googlecode.javacv.cpp.opencv_core.cvLUT;
import static com.googlecode.javacv.cpp.opencv_imgproc.CV_HIST_ARRAY;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvCalcHist;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvCreateHist;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvEqualizeHist;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvReleaseHist;
import static com.googlecode.javacv.cpp.opencv_legacy.cvQueryHistValue_1D;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

import com.googlecode.javacv.cpp.opencv_core.CvMat;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
import com.googlecode.javacv.cpp.opencv_core.IplImageArray;
import com.googlecode.javacv.cpp.opencv_imgproc.CvHistogram;

/**
 * Created with IntelliJ IDEA. User: joey Date: 8/3/14 Time: 下午11:26 To change
 * this template use File | Settings | File Templates.
 */
public class Histogram1D {

    int numberOfBins = 256;

    float minRange = 0.0f;

    float maxRange = 255.0f;

    public void setRanges(float minRange, float maxRange) {
        this.minRange = minRange;
        this.maxRange = maxRange;
    }

    /**
     * Apply a look-up table to an image. It is a wrapper for OpenCV function
     * `cvLUT`.
     * 
     * @param src
     *            input image
     * @param lut
     *            look-up table
     * @return new image
     */
    public static IplImage applyLookup(IplImage src, CvMat lut) {
        // Create out put image of the same size and type as input
        IplImage dest = IplImage.create(cvGetSize(src), src.depth(), src.nChannels());

        // Apply look-up
        cvLUT(src, dest, lut);

        return dest;
    }

    /**
     * Equalize histogram of an image. The algorithm normalizes the brightness
     * and increases the contrast of the image. It is a wrapper for OpenCV
     * function `cvEqualizeHist`.
     * 
     * @param src
     *            input image
     * @return new image
     */
    public static IplImage equalize(IplImage src) {

        // Create out put image of the same size and type as input
        IplImage dest = IplImage.create(cvGetSize(src), src.depth(), src.nChannels());

        // Equalize histogram
        cvEqualizeHist(src, dest);

        return dest;

    }

    /**
     * Computes histogram of an image. This method is `private` since its proper
     * use requires knowledge of inner working of the implementation: # how to
     * extract data from the CvHistogram structure # CvHistogram has to be
     * manually deallocated after use.
     * 
     * @param image
     *            input image
     * @param mask
     *            optional mask
     * @return OpenCV histogram object
     */
    public CvHistogram getHistogram(IplImage image, IplImage mask) {
        // Allocate histogram object
        int dims = 1;
        int[] sizes = new int[] { numberOfBins };
        int histType = CV_HIST_ARRAY;
        float[][] ranges = new float[][] { { minRange, maxRange } };
        CvHistogram hist = cvCreateHist(dims, sizes, histType, ranges, 1);

        // Compute histogram
        int accumulate = 1;
        IplImageArray iarr = new IplImageArray(new IplImage[] { image });
        cvCalcHist(iarr, hist, accumulate, mask);

        return hist;
    }

    /**
     * Computes histogram of an image.
     * 
     * @param image
     *            input image
     * @return histogram represented as an array
     */
    public float[] getHistogramAsArray(IplImage image) {

        // Create and calculate histogram object
        CvHistogram histogram = getHistogram(image, null);

        // Extract values to an array
        float[] dest = new float[numberOfBins];
        for (int bin = 0; bin < numberOfBins; bin++) {
            dest[bin] = cvQueryHistValue_1D(histogram, bin);
        }

        // Release the memory allocated for histogram
        cvReleaseHist(histogram);

        return dest;
    }

    public BufferedImage getHistogramImage(IplImage image) {

        // Output image size
        int width = numberOfBins;
        int height = numberOfBins;

        float[] hist = getHistogramAsArray(image);
        // Set highest point to 90% of the number of bins
        double scale = 0.1 / hist.length * height;

        // Create a color image to draw on
        BufferedImage canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = canvas.createGraphics();

        // Paint background
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);

        // Draw a vertical line for each bin
        g.setColor(Color.BLUE);
        for (int bin = 0; bin < numberOfBins; bin++) {
            int h = (int) Math.round(hist[bin] * scale);
            g.drawLine(bin, height - 1, bin, height - h - 1);
        }

        g.dispose();
        return canvas;
    }

    public BufferedImage getHistogram(CvHistogram histogram) {

        // Output image size
        int width = numberOfBins;
        int height = numberOfBins;

        // Extract values to an array
        float[] hist = new float[numberOfBins];
        for (int bin = 0; bin < numberOfBins; bin++) {
            hist[bin] = cvQueryHistValue_1D(histogram, bin);
        }

        // Set highest point to 90% of the number of bins
        double scale = 0.1 / hist.length * height;

        // Create a color image to draw on
        BufferedImage canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = canvas.createGraphics();

        // Paint background
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);

        // Draw a vertical line for each bin
        g.setColor(Color.BLUE);
        for (int bin = 0; bin < numberOfBins; bin++) {
            int h = (int) Math.round(hist[bin] * scale);
            g.drawLine(bin, height - 1, bin, height - h - 1);
        }

        g.dispose();
        return canvas;
    }
}
